package hn.sigit.view.workflow.transaction;

import hn.sigit.dao.hnd.administrative.HND_TransactionDAO;
import hn.sigit.dao.hnd.cadastre.HND_ParcelDAO;
import hn.sigit.dao.hnd.cadastre.HND_PropertyDAO;
import hn.sigit.dao.hnd.ladmshadow.ParcelDAO;
import hn.sigit.dao.ladm.administrative.LA_RRRDAO;
import hn.sigit.dao.ladm.spatialunit.LA_RequiredRelationshipSpatialUnitDAO;
import hn.sigit.logic.ladmshadow.ShadowToLADM;
import hn.sigit.model.commons.IProperty;
import hn.sigit.model.commons.IRRR;
import hn.sigit.model.hnd.administrative.HND_Transaction;
import hn.sigit.model.hnd.cadastre.HND_Parcel;
import hn.sigit.model.hnd.cadastre.HND_Property;
import hn.sigit.model.hnd.ladmshadow.Parcel;
import hn.sigit.model.hnd.ladmshadow.Property;
import hn.sigit.model.hnd.ladmshadow.RRR;
import hn.sigit.model.ladm.administrative.LA_RRR;
import hn.sigit.model.ladm.spatialunit.ISO19125_Type;
import hn.sigit.model.ladm.spatialunit.LA_RequiredRelationshipSpatialUnit;

import java.util.Date;
import java.util.List;


public class TransactionLogicHelper {
	private TransactionHelper transactionHelper;
	
	public TransactionLogicHelper(TransactionHelper transactionHelper) {
		this.transactionHelper = transactionHelper;
	}
	
	
	private IRRR findRRRById(IProperty property, long id) {
		for (IRRR rrr : property.getRrr())
			if (rrr.getRID() == id)
				return rrr;
		
		return null;
	}
	private IRRR findRRRByLadmId(Property property, long id) {
		for (RRR rrr : property.getRrr())
			if (rrr.getLadmId().longValue() == id)
				return rrr;
		
		return null;
	}
	
	private void addRRRFromProperty(HND_Property dstProperty, Property srcProperty, boolean keepProperty, Date timeStamp) {
		LA_RRR laRrr;
		LA_RRR newLaRrr;
		if (keepProperty) {
			//This means that we do not create a new property, only sync RRRs

			//First, let us invalidate RRRs that were deleted in the transaction
			for (LA_RRR la_rrr : dstProperty.getRrr()) {
				//Check if the object was deleted.... If it is,
				//mark endLifespanVersion with timeStamp
				if ( findRRRByLadmId(srcProperty, la_rrr.getRID()) == null ) {
					la_rrr.setEndLifespanVersion(timeStamp);
					LA_RRRDAO.save(la_rrr);
				}
			}
			
			//add and synch the remaining RRRs
			for (RRR rrr : srcProperty.getRrr()) {
				if (rrr.isModified()) {
					//is it a modified existing one?...
					if (rrr.getLadmId() != null && rrr.getLadmId() != 0) {
						//if it is a modified one we have to endLifespanVersion
						laRrr = (LA_RRR) findRRRById(dstProperty, rrr.getLadmId());
						if (laRrr == null) continue;
						
						laRrr.setEndLifespanVersion(timeStamp);
						LA_RRRDAO.save(laRrr);
					}
					newLaRrr = ShadowToLADM.RRRToLA_RRR(rrr);
					newLaRrr.setBeginLifespanVersion(timeStamp);
					newLaRrr.setEndLifespanVersion(null);
					newLaRrr.setBaunit(dstProperty);
					
					dstProperty.getRrr().add(newLaRrr);
					
					LA_RRRDAO.save(newLaRrr);
				}
			}
			
			
		}
		else {
			for (RRR rrr : srcProperty.getRrr()) {
				//Add everything to dstProperty. Since it is a new Property
				//then everything's beginLifespanVersion is set to current timeStamp
				newLaRrr = ShadowToLADM.RRRToLA_RRR(rrr);
				newLaRrr.setBeginLifespanVersion(timeStamp);
				newLaRrr.setEndLifespanVersion(null);
				
				newLaRrr.setBaunit(dstProperty);
				dstProperty.getRrr().add(newLaRrr);
				
				LA_RRRDAO.save(newLaRrr);
			}
		}
		HND_PropertyDAO.save(dstProperty);
	}
	
	private void replaceParcel(HND_Parcel newHndParcel, Parcel oldParcel, Date timeStamp,
			boolean keepProperty) {
		HND_Parcel oldHndParcel;
		HND_Property oldHndProperty = null;
		
		
		oldHndParcel = HND_ParcelDAO.loadParcelById(oldParcel.getLadmId());
		oldHndParcel.setEndLifespanVersion(timeStamp);
		if (!keepProperty) {
			oldHndProperty = oldHndParcel.getProperty();
			oldHndProperty.setEndLifespanVersion(timeStamp);
		}
		
		//Make required relationship
		LA_RequiredRelationshipSpatialUnit newReqRelSpUnit = new LA_RequiredRelationshipSpatialUnit();
		newReqRelSpUnit.setRelationship(ISO19125_Type.ST_INTERSECTS);
		
		//Add predecessor to newHndParcel
		newHndParcel.getRequiredRelationshipSpatialUnits1().add(newReqRelSpUnit);
		
		//Add successor to oldHndParcel
		oldHndParcel.getRequiredRelationshipSpatialUnits2().add(newReqRelSpUnit);
		
		newReqRelSpUnit.setSpatialUnit1(newHndParcel);
		newReqRelSpUnit.setSpatialUnit2(oldHndParcel);
		
		HND_ParcelDAO.save(oldHndParcel);
		HND_ParcelDAO.save(newHndParcel);
		
		if (!keepProperty) {
			HND_PropertyDAO.save(oldHndProperty);
			HND_PropertyDAO.save(newHndParcel.getProperty());
		}
		
		LA_RequiredRelationshipSpatialUnitDAO.save(newReqRelSpUnit);
	}
	
	public void applyPresentationChanges() {
		HND_Transaction transaction = transactionHelper.getTransaction();
		long transactionId = transaction.getPresentationId();
		
		Date timeStamp = new Date();
		List<Parcel> originalParcelList = ParcelDAO.loadOriginalParcelsByPresentationId(transactionId);
		List<Parcel> finalParcelList = ParcelDAO.loadFinalParcelsByPresentationId(transactionId);
		
		HND_Property newHndProperty, hndProperty;
		HND_Parcel newHndParcel;
		for (Parcel newParcel : finalParcelList) {
			
			/*
			 * CASE 1: Modified parcel having only its text attributes and
			 * shape geometry modified. Applies to both requested and neighbor parcels.
			 * A new parcel is created having the previous one as a predecessor.
			 * !!!! No new Property is created and rights are just synched !!!!
			 */
			if (newParcel.isModified() && !newParcel.isOriginal()) {
				/*
				 * CASE 1.1: RRRs were NOT modified
				 */
				newHndParcel = ShadowToLADM.ParcelToHND_Parcel(newParcel);
				hndProperty = HND_PropertyDAO.loadPropertyByID(newParcel.getProperty().getLadmId());
				
				newHndParcel.setBeginLifespanVersion(timeStamp);
				newHndParcel.getBaunits().add(hndProperty);
				
				hndProperty.getSpatialUnits().add(newHndParcel);
				
				transaction.getOriginatedSpatialZones().add(newHndParcel);
				newHndParcel.setOriginatingTransaction(transaction);

				replaceParcel(newHndParcel, newParcel, timeStamp, true);
				HND_PropertyDAO.save(hndProperty);
				
				addRRRFromProperty(hndProperty, newParcel.getProperty(), true, timeStamp);
				
				//TODO: Consider the case when rights were modified. New property is required. Extra case
				/*
				 * CASE 1.2: RRRs were indeed modified. New Property required!
				 */
			}
			/*
			 * CASE 2: New parcel with predecessor(s). New Property and Parcel
			 * objects are created.
			 */
			else if (!newParcel.isModified() && !newParcel.isOriginal()) {
				newHndParcel = ShadowToLADM.ParcelToHND_Parcel(newParcel);
				newHndProperty = ShadowToLADM.PropertyToHND_Property(newParcel.getProperty());
				
				newHndParcel.setBeginLifespanVersion(timeStamp);
				newHndParcel.getBaunits().add(newHndProperty);
				
				newHndProperty.setBeginLifespanVersion(timeStamp);
				newHndProperty.getSpatialUnits().add(newHndParcel);
				
				transaction.getOriginatedSpatialZones().add(newHndParcel);
				newHndParcel.setOriginatingTransaction(transaction);
				
				if (originalParcelList != null && originalParcelList.size() > 0) {
					for (Parcel oldParcel : originalParcelList) {
						if ( newParcel.getShape().intersection(oldParcel.getShape()).getArea() > 0.0001 ) {
							replaceParcel(newHndParcel, oldParcel, timeStamp, false);
						}
					}
				}
				else {
					//just save the parcel and property
					HND_ParcelDAO.save(newHndParcel);
					HND_PropertyDAO.save(newHndProperty);
				}
				//Since this is a brand new property, we do not carry history
				//from its predecessors
				addRRRFromProperty(newHndProperty, newParcel.getProperty(), false, timeStamp);
			}
			/*
			 * CASE 3: No modification during transaction. We still have to synch
			 * any changed RRR.
			 * However, in this case we only need to synch only requested parcels, no neighbors, as
			 * neighbors' RRRs should not have changed.
			 */
			else if (!newParcel.isModified() && newParcel.isOriginal()) {
				if (!newParcel.isReadOnly()) {
					hndProperty = HND_PropertyDAO.loadPropertyByID(newParcel.getProperty().getLadmId());
					addRRRFromProperty(hndProperty, newParcel.getProperty(), true, timeStamp);
				}
			}
		}
		HND_TransactionDAO.save(transaction);
	}

}
